#include <string.h>   // 提供 NULL strlen()
#include <stdbool.h>
#include <stdio.h>//snprintf，printf
#include <stdlib.h>//malloc
#include <stdarg.h>// ❌call to undeclared function 'va_start'；✅引入函数声明的头文件：#include <stdarg.h>

/**
 * 判断字符数组是否为空
 * @param str 字符数组
 * @return bool 字符数组是否为空
 */
_Bool is_empty_str(const char *str) {
    return (str == NULL) || (str[0] == '\0');
}

/**
 * 判断是否为Windows系统
 * @param void
 * @return bool 是否为Windows系统
 */
_Bool is_windows_sys() {
    _Bool is_windows = false;
    #if defined(_WIN32) || defined(_WIN64)
        is_windows = true;
    #endif
    return is_windows;
}

/**
 * str1尾部附加str2组成新的字符数组
 *
 * char *new_str = plus_str(str1, str2);
 * ✅返回的 char* 是堆内存，调用者必须在使用完后调用free释放堆内存，❌否则每次调用都会泄漏内存
    lv_label_set_text(label, new_str);
    free(new_str); // 必须 free

 * @param str1 字符数组1
 * @param str2 附加到str1尾部的字符数组2
 * @return char * 字符数组
 */
char *plus_str(const char *str1, const char *str2) {
    if (!str1) str1 = "";
    if (!str2) str2 = "";
    int total_size = strlen(str1) + strlen(str2) + 1;// strlen计算字符串的长度（不包括 \0 结尾），因此需要加1用于存放结束字符\0
    char *str = (char*)malloc(total_size);// malloc用于在堆上申请 total_size 个字节的内存。返回指向这块内存的指针。
    //C++ 需要通过(char*)强制类型转换，因为 malloc 返回 void*。在纯 C 中可以不加。
    if (!str) return NULL;  // malloc 失败直接返回 NULL
    snprintf(str, total_size, "%s%s", str1, str2);
    return str;
}

/**
 * 类似 printf() 的通用函数
 * 使用示例：
char *url = build_string("https://%s:%d/api?value=%.2f&name=%s",
                            "example.com", 8080, 3.14159, "test");
if (url) {
    printf("%s\n", url);
    lv_label_set_text(label, url);
    free(url);//✅返回的 char* 是堆内存，调用者必须在使用完后调用free释放堆内存，❌否则每次调用都会泄漏内存
}
 *
 * @param format 任意格式字符串
 * @param ... 可变参数 ...，接收任意数量的参数
 * @return char * 字符数组
 */
char * build_string(const char *format, ...) {
    va_list args;
    va_start(args, format);

    // 第一次调用 vsnprintf 计算所需长度
    int len = vsnprintf(NULL, 0, format, args);
    va_end(args);

    if (len < 0) return NULL;

    // 在堆上分配内存（多 1 字节放 '\0'），用完之后需要free释放内存避免内存泄露
    char *buffer = malloc(len + 1);
    if (!buffer) return NULL;

    // 第二次真正写入内容
    va_start(args, format);
    vsnprintf(buffer, len + 1, format, args);
    va_end(args);

    return buffer;
}


/**
 * str1尾部附加str2组成新的字符数组
 * char *new_str = merge_str(str1, str2);
 * @param str1 字符数组1
 * @param str2 附加到str1尾部的字符数组2
 * @return char * 字符数组
 */
//❌Windows不支持，注释掉，改用plus_str
// char *merge_str(const char *str1, const char *str2) {
    //MacOS支持，但MSVC (Windows) ❌ 不支持 VLA 改用 malloc()
    //报错❌：expression must have a constant value (C/C++(28))，出现在 定义数组时使用了非常量表达式。
    // 因此Windows系统用编译器常量来初始化字符数组明确内存长度
    // #ifdef _WIN32
    //     char str[512];
    //     //用 sprintf 拼接成一个新字符串
    //     sprintf(str, "%s%s", str1, str2);
    //     return str;
    // #else
    //     int total_size = strlen(str1) + strlen(str2) + 1;
    //     char str[total_size];
    //     //用 sprintf 拼接成一个新字符串
    //     sprintf(str, "%s%s", str1, str2);
    //     return str;
    // #endif

    // 也可以通过空格拼接，但是只能针对宏定义和字面量，字面量就是直接写在代码上
    // printf("str1 + str2: %s\n", "str1" "str2");
// }
